package show.me.the.money.util.context;



import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.*;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;

@SuppressWarnings("unchecked")
public class MongoDBObjectContext extends Context {
	private DBObject data;
	private static Pattern pattern = Pattern.compile("(.+)\\[(\\d+)\\]");
	public MongoDBObjectContext(DBObject data){
		super();
		this.data = data;
	}
	
	public boolean has(String name){
		int n = name.indexOf(".");
		if(n > -1){
			DiveResult r = dive(name);
			return r != null;
		}
		else{
			return data.containsField(name);
		}
	}
	
	public void put(String name,Object value){
		int n = name.indexOf(".");
		if(n > -1){
			DiveResult r = dive(name);
			if(r != null){
				r.setValue(value);
			}		
		}
		else{
			data.put(name, value);
		}
	}
	
	public Object get(String name,HashMap<String,Object> arg){
		int n = name.indexOf(".") + name.indexOf("[");
		if(n > -2){
			DiveResult r = dive(name);
			if(r != null){
				return r.getValue();
			}
			return null;
		}
		return data.get(name);
	}
	
	private DiveResult dive(String name){
		String[] names = name.split("\\.");
		DBObject o = data;
		try{
			int n = names.length;
			for(int i = 0; i < n; i ++){
				String nm =names[i];
				//find if Array exist
				Matcher m = pattern.matcher(nm);
				String key = nm;
				List<Integer> indexes = new ArrayList<Integer>();
				while(m.find()){
					if(m.groupCount() == 2){
						int index = Integer.parseInt(m.group(2));
						indexes.add(index);
						key = m.group(1);
					}
					m = pattern.matcher(m.group(1));
				}
				int size = indexes.size();
				if(size > 0){ //process arrary
					if(o.containsField(key)){
						int index = indexes.get(size - 1);
						if(i == n - 1){ //process last one
							if(size > 1){  //muti Array at last position
								List ls = (List)((List)o.get(key)).get(index);
								for(int j = size - 2; j >= 0; j --){
									index = indexes.get(j);
									if(j == 0){ //at the result point
										return new ArrayRST(ls,index);
									}
									else{
										ls = (List)ls.get(index); 
									}
								}
							}
							else{  //Single Array at the result point
								return new ArrayRST((List)o.get(key),index);
							}
						}
						else{  //in the mid position,pass the token o;
							if(size > 1){ //muti Array at mid position
								List ls = (List)((List)o.get(key)).get(index);
								for(int j = size - 2; j >= 0; j --){
									index = indexes.get(j);
									if(j == 0){
										o =  (DBObject)ls.get(index);
									}
									else{
										ls = (List)ls.get(index); 
									}
								}									
							}
							else{ //Single Array at mid position
								o = (DBObject)((List)o.get(key)).get(index);
							}
						}
					}	
				}
				else{  //process JsonObject
					if(i == n - 1){//at the result point
						return new DBObjectRST(o,nm);
					}
					else{
						o = (DBObject)o.get(nm);
					}
				}
			}
			return null;
		}
		catch(Exception e){
			e.printStackTrace();
			return null;
		}		
	}
	
	public String value(String name,HashMap<String,Object> arg){
		return String.valueOf(get(name,arg));
	}
	
	public static void main(String[] args){
		DBObject h = new BasicDBObject();
		
		DBObject h3 = new BasicDBObject();
		h3.put("name", "sean220");
		
		List ls = new ArrayList();
		ls.add("a");
		ls.add("b");
		ls.add("c");
		
		List ls2 = new ArrayList();
		ls2.add("a1");
		ls2.add("b2");
		ls2.add("c3");
		ls2.add(h3);
		
		ls.add(ls2);
		
		h.put("name", "sean");
		
		
		DBObject h2 = new BasicDBObject();
		h2.put("h21","21");
		h2.put("h22","22");
		h.put("arrs", ls);
		
		h.put("h2", h2);
		
		Context ctx = new MongoDBObjectContext(h);
		System.out.println(ctx.value("arrs[3][3].name"));
		
		System.out.println(ctx.value("h2.h21"));
	}
}



class DBObjectRST implements DiveResult{
	private DBObject o;
	private String name;
	
	public DBObjectRST(DBObject o,String n){
		this.o = o;
		name = n;
	}
	
	public Object getValue() {
		return o.get(name);
	}

	public void setValue(Object v) {
		o.put(name, v);
	}
}

